package cultu.test.tool;

import static org.junit.Assert.assertEquals;

import java.io.IOException;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.dbunit.Assertion;
import org.dbunit.DatabaseUnitException;
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseDataSourceConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.filter.DefaultColumnFilter;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.ext.hsqldb.HsqldbDataTypeFactory;
import org.dbunit.operation.DatabaseOperation;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * Helper for DbUnit operations.
 * @author Arnaud Cogoluegnes
 *
 */
public class DbUnitHelper {
	
	private static final String VERIFICATION_EXCEPTION = "Error during verification";

	/** datasource */
	private DataSource ds;
	
	@Autowired
	public DbUnitHelper(DataSource ds) {
		this.ds = ds;
	}
	
	public DbUnitHelper() { }
	
	private IDatabaseConnection getConnection() throws Exception {
		DatabaseDataSourceConnection datasourceConnection = new DatabaseDataSourceConnection(
				ds
		);
		// avoid the type not recognized problem
		// http://dbunit.sourceforge.net/faq.html#typenotrecognized
		DatabaseConfig config = datasourceConnection.getConfig();
	    config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,
	            new HsqldbDataTypeFactory()
	    );
		return datasourceConnection;
	}
	
	/**
	 * Retrieve a dataset.
	 * @return
	 * @throws Exception
	 */
	public IDataSet getDataSet(String dataSetResourcePath) throws Exception {
		IDataSet dataset = new FlatXmlDataSet(getClass().getResourceAsStream(
				dataSetResourcePath));
		return dataset;
	}	
	
	/**
	 * Do a clean insert.
	 * @param dataSet
	 */
	public void doCleanInsert(IDataSet dataSet) {
		try {
			DatabaseOperation.CLEAN_INSERT.execute(
					getConnection(),
					dataSet
			);
		} catch (Exception e) {
			throw new RuntimeException("Error while doing a clean insert : "+e.getMessage(),e);
		}
	}
	
	/**
	 * Table content and dataset comparison.
	 * Comparison is based on the dataset columns.
	 * @param tableName
	 * @param dataSet
	 */
	public void compareTables(String tableName, IDataSet dataSet) {	
		try {
			// retrieve table content
			IDataSet databaseDataSet = getConnection().createDataSet();
			ITable currentTable = databaseDataSet.getTable(tableName);

			// what we are expecting (from the dataset)
			ITable expectedTable = dataSet.getTable(tableName);
			
			// filter from the dataset content
			currentTable = DefaultColumnFilter.includedColumnsTable(currentTable,expectedTable.getTableMetaData().getColumns());

			// assertion
			Assertion.assertEquals(expectedTable, currentTable);
		} catch (DataSetException e) {
			throw new RuntimeException(VERIFICATION_EXCEPTION, e);
		} catch (SQLException e) {
			throw new RuntimeException(VERIFICATION_EXCEPTION, e);
		} catch (DatabaseUnitException e) {
			throw new RuntimeException(VERIFICATION_EXCEPTION, e);
		} catch (Exception e) {
			throw new RuntimeException(VERIFICATION_EXCEPTION, e);
		}
	}

	/**
	 * Check row count in a table.
	 * @param tableName
	 * @param expectedCount
	 */
	public void compareTableCount(String tableName,int expectedCount) {
		try {
			IDataSet databaseDataSet = getConnection().createDataSet();
			ITable currentTable = databaseDataSet.getTable(tableName);
			assertEquals("le nb incorect d'element dans la table "+tableName,expectedCount,currentTable.getRowCount());
		} catch (DataSetException e) {
			throw new RuntimeException(VERIFICATION_EXCEPTION, e);
		} catch (SQLException e) {
			throw new RuntimeException(VERIFICATION_EXCEPTION, e);
		} catch (Exception e) {
			throw new RuntimeException(VERIFICATION_EXCEPTION, e);
		}		
	}

	/**
	 * Table content and dataset comparison.
	 * Comparison is based on the dataset columns.
	 * @param tableName
	 * @param ressourceDataSet
	 */
	public void compareTables(String tableName, String ressourceDataSet) {
		IDataSet dataSet = null;
		try {
			dataSet = new FlatXmlDataSet(getClass().getResourceAsStream(
					ressourceDataSet));
		} catch (DataSetException e) {
			throw new RuntimeException(VERIFICATION_EXCEPTION, e);
		} catch (IOException e) {
			throw new RuntimeException(VERIFICATION_EXCEPTION, e);
		}
		compareTables(tableName, dataSet);
	}
	
	/**
	 * Retrieve a table from the database.
	 * @param tableName
	 * @return
	 */
	public ITable getTable(String tableName) {
		try {
			IDataSet databaseDataSet = getConnection().createDataSet();
			return databaseDataSet.getTable(tableName);
		} catch (DataSetException e) {
			throw new RuntimeException(VERIFICATION_EXCEPTION, e);
		} catch (SQLException e) {
			throw new RuntimeException(VERIFICATION_EXCEPTION, e);
		} catch (Exception e) {
			throw new RuntimeException(VERIFICATION_EXCEPTION, e);
		}		
	}

    public void doDeleteAll(IDataSet dataSet)
    {
        try {
            DatabaseOperation.DELETE_ALL.execute(
                    getConnection(),
                    dataSet
            );
        } catch (Exception e) {
            throw new RuntimeException("Error while doing a delete all : "+e.getMessage(),e);
        }
        
    }
	
}
